#include "stdafx.h"
#include "./jpegdecoder.h"

JpegDecoder::JpegDecoder(byte* buffer, size_t size)
{
	m_jdata.bSucceeded = false;

	if (!CheckFormat(buffer, size))
	{
		return;
	}

	m_srcmgr.buffer = buffer;
	m_srcmgr.bufSize = size;

	StartDecode();
}

JpegDecoder::~JpegDecoder()
{

}

ImageData JpegDecoder::GetImageData()
{
	return m_jdata;
}

bool JpegDecoder::CheckFormat(const byte * buffer, size_t size)
{
	if (!buffer || size < 3)
	{
		return false;
	}

	return buffer[0] == 0xff &&
		   buffer[1] == 0xd8 &&
		   buffer[2] == 0xff;
}

bool JpegDecoder::StartDecode()
{
	m_srcmgr.init_source = JpegInitSource;
	m_srcmgr.fill_input_buffer = JpegFillInputBuffer;
	m_srcmgr.skip_input_data = JpegSkipInputData;
	m_srcmgr.resync_to_restart = jpeg_resync_to_restart;
	m_srcmgr.term_source = JpegTermSource;

	m_srcmgr.cur = m_srcmgr.buffer;

	jpeg_create_decompress(&m_cinfo);
	m_cinfo.err = jpeg_std_error(&m_jerr);
	m_cinfo.src = &m_srcmgr;

	jpeg_read_header(&m_cinfo, TRUE);
	jpeg_start_decompress(&m_cinfo);

	ReadImageData();

	jpeg_destroy_decompress(&m_cinfo);

	return true;
}

void JpegDecoder::ReadImageData()
{
	m_jdata.nWidth = m_cinfo.output_width;
	m_jdata.nHeight = m_cinfo.output_height;

	unsigned int nRowSize = m_cinfo.output_width * m_cinfo.output_components;

	m_jdata.nSize = m_cinfo.output_width * m_cinfo.output_height * 4;
	m_jdata.pBuffer = new byte[m_jdata.nSize + 1];
		
	byte* pByBitmap = m_jdata.pBuffer + m_jdata.nSize - m_cinfo.output_width * 4;
	JSAMPARRAY pBuffer = (*m_cinfo.mem->alloc_sarray)((j_common_ptr)&m_cinfo, JPOOL_IMAGE, nRowSize, 1);

	while (m_cinfo.output_scanline < m_cinfo.output_height)
	{
		jpeg_read_scanlines(&m_cinfo, pBuffer, 1);

		byte* pSrc = pBuffer[0];
		byte* pLine = pByBitmap;

		if (m_cinfo.num_components == 4)
		{
			for (size_t x = 0; x < m_cinfo.output_width; ++x)
			{
				byte k = *(pSrc + 3);
				pLine[3] = 0xFF;
				pLine[2] = (byte)((*(pSrc + 2)/255));
				pLine[1] = (byte)((*(pSrc + 1)/255));
				pLine[0] = (byte)((*(pSrc + 0)/255));
				pSrc += 4;
				pLine += 4;
			}
		}
		else if (m_cinfo.num_components == 3)	//RGB
		{
			for (size_t x = 0; x < m_cinfo.output_width; ++x)
			{
				pLine[3] = 0xFF;
				pLine[2] = *(pSrc+2);
				pLine[1] = *(pSrc+1);
				pLine[0] = *(pSrc+0);
				pSrc += 3;
				pLine+= 4;
			}
		}
		pByBitmap -= m_cinfo.output_width * 4;
	}
	jpeg_finish_decompress(&m_cinfo);

	m_jdata.bSucceeded = true;
}

void JpegDecoder::JpegInitSource(j_decompress_ptr cinfo)
{
	SourceMgr* jpegSrcMgr = (SourceMgr*)cinfo->src;
	jpegSrcMgr->next_input_byte = jpegSrcMgr->buffer;
	jpegSrcMgr->bytes_in_buffer = 0;
}

boolean JpegDecoder::JpegFillInputBuffer(j_decompress_ptr cinfo)
{
	SourceMgr* jpegSrcMgr = (SourceMgr*)cinfo->src;

	jpegSrcMgr->next_input_byte = jpegSrcMgr->cur;
	jpegSrcMgr->bytes_in_buffer = jpegSrcMgr->bufSize;

	return true;
}

void JpegDecoder::JpegSkipInputData(j_decompress_ptr cinfo, long num_bytes)
{
	SourceMgr* jpegSrcMgr = (SourceMgr*)cinfo->src;

	if (num_bytes <= 0)
	{
		return;
	}

	if (static_cast<size_t>(num_bytes) > jpegSrcMgr->bytes_in_buffer)
	{
		jpegSrcMgr->cur += num_bytes - jpegSrcMgr->bytes_in_buffer;
		jpegSrcMgr->bytes_in_buffer = 0;
	}
	else
	{
		jpegSrcMgr->bytes_in_buffer -= num_bytes;
		jpegSrcMgr->next_input_byte += num_bytes;
	}
}

void JpegDecoder::JpegTermSource(j_decompress_ptr cinfo)
{
	SourceMgr* jpegSrcMgr = (SourceMgr*)cinfo->src;
}